package astar;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.Vector;

import domain.Agent;
import domain.AgentSmart;
import domain.Board;
import domain.Debug;
import domain.Flag;
import domain.Game;
import domain.Game.action;

/**
 *
 * @author Shai Givony
 */
public class AStar {

    private AgentSmart				_agentMe;
    private PriorityQueue<ANode>    _priorityQ;
    private Set<ANode> 				_history;
    private int 					_counter;
    
    public AStar(AgentSmart agentMe) {
        
    	this._agentMe 	= agentMe;
        this._priorityQ = new PriorityQueue<ANode>();
        this._counter 	= 41;								//TODO: think about it
        this._history 	= new HashSet<ANode>();				// to avoid states duplication
    }
    
    public LinkedList<action> runAStarSearch(Board board, Vector<Agent> agents, Vector<Flag> flags){
        LinkedList<action> ans = new LinkedList<action>();
        
        //constructing first node of A* tree:
        ANode root = new ANode(Game._board, this._agentMe, agents, flags, this._agentMe.getScore(), ans);
        _priorityQ.add(root);
        ANode nodeWithMinimalF = _priorityQ.poll();		//extract with minimal f function
        
        while(!endCondition(nodeWithMinimalF)){
        	
            Debug.println("node with minimal f: " + nodeWithMinimalF);
            if(!State_Exist(nodeWithMinimalF)){//preventing duplicated states.
            	
            	this._history.add(nodeWithMinimalF);// adding the ANode to the history.
	            
            	//Extract:
	            ANode after_up      = nodeWithMinimalF.simulateNode(action.UP);
	            ANode after_right   = nodeWithMinimalF.simulateNode(action.RIGHT);
	            ANode after_down    = nodeWithMinimalF.simulateNode(action.DOWN);
	            ANode after_left    = nodeWithMinimalF.simulateNode(action.LEFT);
	            
	            Debug.println("Sentry at: " + after_up.getAgents().elementAt(0).getSquare());
	            Debug.println(after_up + "\n" + after_right + "\n" + after_down + "\n" + after_left + "\n");
	            
	            if(after_up!=null){
	                _priorityQ.add(after_up);
	            }
	            if(after_right!=null){
	               _priorityQ.add(after_right);
	            }
	            if(after_down!=null){
	                _priorityQ.add(after_down);
	            }
	            if(after_left!=null){
	                _priorityQ.add(after_left);
	            }
	            //end of extract
            }
            nodeWithMinimalF = _priorityQ.poll();//extract with minimal f function
        }
        ans = nodeWithMinimalF.getPath();
        Debug.println("I will go: "+ans);
        //ans = _priorityQ.peek().getPath();
        return ans;
    }

    private boolean State_Exist(ANode a) {
		return this._history.contains(a);
	}

	private boolean endCondition(ANode nodeWithMinimalF) {
        this._counter--;
		return nodeWithMinimalF.isGoalNode() || this._counter == 0;
        //TODO: check if there is more flag
        //TODO: check if you are dead
    }
    
    
}
